package file

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"go.uber.org/zap"
	"guns-go/global"
	"guns-go/model/common"
	"io"
	"io/ioutil"
)

type AliYunOSS struct{}

//初始化操作的客户端
func (*AliYunOSS) InitClient() error {

	client, err := oss.New(global.GVA_CONFIG.AliyunOSS.Endpoint, global.GVA_CONFIG.AliyunOSS.AccessKeyId,
		global.GVA_CONFIG.AliyunOSS.AccessKeySecret)

	if err != nil {
		return err
	}

	global.GVA_OSS = client

	return nil
}

//销毁操作的客户端
func (*AliYunOSS) DestroyClient() error {
	return nil
}

//获取操作的客户端
func (*AliYunOSS) GetClient() (error, interface{}) {
	return nil, global.GVA_OSS
}

//查询存储桶是否存在
func (*AliYunOSS) DoesBucketExist(bucketName string) (bool, error) {
	return global.GVA_OSS.IsBucketExist(bucketName)
}

//设置预定义策略
func (*AliYunOSS) SetBucketAcl(bucketName string, bucketAuthEnum common.BucketAuthType) (err error) {

	if common.Private == bucketAuthEnum {
		err = global.GVA_OSS.SetBucketACL(bucketName, oss.ACLPrivate)
	} else if common.PublicRead == bucketAuthEnum {
		err = global.GVA_OSS.SetBucketACL(bucketName, oss.ACLPublicRead)
	} else if common.PublicReadWrite == bucketAuthEnum {
		err = global.GVA_OSS.SetBucketACL(bucketName, oss.ACLPublicReadWrite)
	}

	return err
}

//判断是否存在文件
func (*AliYunOSS) IsExistingFile(bucketName string, key string) (bool, error) {

	bucket, err := NewBucket(bucketName)
	if err != nil {
		global.GVA_LOG.Error("function AliyunOSS.NewBucket() Failed", zap.Any("err", err.Error()))
		return false, errors.New("function AliyunOSS.NewBucket() Failed, err:" + err.Error())
	}

	// 判断文件是否存在。
	isExist, err := bucket.IsObjectExist(key)
	if err != nil {
		fmt.Println("Error:", err)
		return false, err
	}

	return isExist, err
}

//存储文件
func (*AliYunOSS) StorageFile(bucketName string, key string, byteS []byte) error {

	bucket, err := NewBucket(bucketName)
	if err != nil {
		global.GVA_LOG.Error("function AliyunOSS.NewBucket() Failed", zap.Any("err", err.Error()))
		return errors.New("function AliyunOSS.NewBucket() Failed, err:" + err.Error())
	}

	return bucket.PutObject(key, bytes.NewReader(byteS))
}

//存储文件（存放到指定的bucket里边）
func (*AliYunOSS) StorageFileWithReader(bucketName string, key string, reader io.Reader) error {

	bucket, err := NewBucket(bucketName)
	if err != nil {
		global.GVA_LOG.Error("function AliyunOSS.NewBucket() Failed", zap.Any("err", err.Error()))
		return errors.New("function AliyunOSS.NewBucket() Failed, err:" + err.Error())
	}

	return bucket.PutObject(key, reader)
}

//获取某个bucket下的文件字节
func (*AliYunOSS) GetFileBytes(bucketName string, key string) ([]byte, error) {

	bucket, err := NewBucket(bucketName)
	if err != nil {
		global.GVA_LOG.Error("function AliyunOSS.NewBucket() Failed", zap.Any("err", err.Error()))
		return nil, errors.New("function AliyunOSS.NewBucket() Failed, err:" + err.Error())
	}

	body, err := bucket.GetObject(key)

	if err != nil {
		return nil, err
	}

	// 数据读取完成后，获取的流必须关闭，否则会造成连接泄漏，导致请求无连接可用，程序无法正常工作。
	defer body.Close()

	b, err := ioutil.ReadAll(body)

	if err != nil {
		return nil, err
	}

	return b, err
}

//文件访问权限管理
func (*AliYunOSS) SetFileAcl(bucketName string, key string, bucketAuthEnum common.BucketAuthType) error {

	bucket, err := NewBucket(bucketName)
	if err != nil {
		global.GVA_LOG.Error("function AliyunOSS.NewBucket() Failed", zap.Any("err", err.Error()))
		return errors.New("function AliyunOSS.NewBucket() Failed, err:" + err.Error())
	}

	if common.Private == bucketAuthEnum {
		err = bucket.SetObjectACL(key, oss.ACLPrivate)
	} else if common.PublicRead == bucketAuthEnum {
		err = bucket.SetObjectACL(key, oss.ACLPublicRead)
	} else if common.PublicReadWrite == bucketAuthEnum {
		err = bucket.SetObjectACL(key, oss.ACLPublicReadWrite)
	}

	return err
}

//拷贝文件
func (*AliYunOSS) CopyFile(originBucketName string, originFileKey string, newBucketName string, newFileKey string) error {

	bucket, err := NewBucket(originBucketName)
	if err != nil {
		global.GVA_LOG.Error("function AliyunOSS.NewBucket() Failed", zap.Any("err", err.Error()))
		return errors.New("function AliyunOSS.NewBucket() Failed, err:" + err.Error())
	}

	// 从本存储空间拷贝源文件（srcObjectName）到其它存储空间（dstBucketName）。
	_, err = bucket.CopyObjectTo(newBucketName, newFileKey, originFileKey)
	if err != nil {
		fmt.Println("CopyObjectTo Error:", err)
		return err
	}

	return nil
}

//获取文件的下载地址（带鉴权的），生成外网地址
func (*AliYunOSS) GetFileAuthUrl(bucketName string, key string, timeoutMillis int64) (string, error) {

	bucket, err := NewBucket(bucketName)
	if err != nil {
		global.GVA_LOG.Error("function AliyunOSS.NewBucket() Failed", zap.Any("err", err.Error()))
		return "", errors.New("function AliyunOSS.NewBucket() Failed, err:" + err.Error())
	}

	return bucket.SignURL(key, oss.HTTPGet, timeoutMillis)
}

//删除文件
func (*AliYunOSS) DeleteFile(bucketName string, key string) error {

	bucket, err := NewBucket(bucketName)
	if err != nil {
		global.GVA_LOG.Error("function AliyunOSS.NewBucket() Failed", zap.Any("err", err.Error()))
		return errors.New("function AliyunOSS.NewBucket() Failed, err:" + err.Error())
	}

	// 删除单个文件。objectName表示删除OSS文件时需要指定包含文件后缀在内的完整路径，例如abc/efg/123.jpg。
	// 如需删除文件夹，请将objectName设置为对应的文件夹名称。如果文件夹非空，则需要将文件夹下的所有object删除后才能删除该文件夹。
	return bucket.DeleteObject(key)
}

func NewBucket(bucketName string) (*oss.Bucket, error) {

	// 获取存储空间。
	bucket, err := global.GVA_OSS.Bucket(bucketName)
	if err != nil {
		return nil, err
	}

	return bucket, nil
}
